using System;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace FileUtility.FTP.FtpSupport
{
	[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto,Pack=4)]
	public struct Win32FindData 
	{
		public FileAttributes fileAttributes;
		private long ftCreationTime;
		private long ftLastAccessTime;
		private long ftLastWriteTime;
		private int nFileSizeHigh;
		private int nFileSizeLow;
		int dwReserved0;
		int dwReserved1;
		[MarshalAs(UnmanagedType.ByValTStr, SizeConst=NativeMethods.MAX_PATH)]
		public string FileName;
		[MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)]
		public string AlternateFileName;

		public long FileSize
		{
			get
			{
				long temp = ((long)nFileSizeHigh) << 32;
				temp |= (uint)nFileSizeLow;
				return temp;
			}
		}

		public DateTime CreationTime
		{
			get{ return DateTime.FromFileTime(ftCreationTime); }
		}
		public DateTime LastAccessTime
		{
			get{ return DateTime.FromFileTime(ftLastAccessTime); }
		}
		public DateTime LastWriteTime
		{
			get{ return DateTime.FromFileTime(ftLastWriteTime); }
		}
	}

	public enum InternetPort
	{
		InvalidPortNumber    = 0,   
		DefaultFtpPort       = 21,  
		DefaultGopherPort    = 70,  
		DefaultHttpPort      = 80,  
		DefaultHttpsPort     = 443, 
		DefaultSocksPort     = 1080,
	}

	public enum InternetService
	{
		Ftp    = 1,
		Gopher = 2,
		Http   = 3,
	}

	public enum InternetOpenType
	{
		Preconfig                    = 0,   // use registry configuration
		Direct                       = 1,   // direct to net
		Proxy                        = 3,   // via named proxy
		PreconfigWithNoAutoproxy     = 4,   // prevent using java/script/INS
	}

	public enum FtpTransferType
	{
		Ascii  = 0x00000001,
		Binary = 0x00000002,
	}

	public enum GenericRights
	{
		Read = unchecked((int)0x80000000),
		Write = 0x40000000,
	}


	public class NativeMethods
	{
		#region Constants
		internal const int MAX_PATH = 260;
		internal const int INTERNET_ERROR_BASE                   = 12000;
		internal const int INTERNET_ERROR_FIRST                  = 12000;
		internal const int INTERNET_ERROR_LAST                   = 12174;

		public const int ERROR_INTERNET_OUT_OF_HANDLES           = 12001;
		public const int ERROR_INTERNET_TIMEOUT                  = 12002;
		public const int ERROR_INTERNET_EXTENDED_ERROR           = 12003;
		public const int ERROR_INTERNET_INTERNAL_ERROR           = 12004;
		public const int ERROR_INTERNET_INVALID_URL              = 12005;
		public const int ERROR_INTERNET_UNRECOGNIZED_SCHEME      = 12006;
		public const int ERROR_INTERNET_NAME_NOT_RESOLVED        = 12007;
		public const int ERROR_INTERNET_PROTOCOL_NOT_FOUND       = 12008;
		public const int ERROR_INTERNET_INVALID_OPTION           = 12009;
		public const int ERROR_INTERNET_BAD_OPTION_LENGTH        = 12010;
		public const int ERROR_INTERNET_OPTION_NOT_SETTABLE      = 12011;
		public const int ERROR_INTERNET_SHUTDOWN                 = 12012;
		public const int ERROR_INTERNET_INCORRECT_USER_NAME      = 12013;
		public const int ERROR_INTERNET_INCORRECT_PASSWORD       = 12014;
		public const int ERROR_INTERNET_LOGIN_FAILURE            = 12015;
		public const int ERROR_INTERNET_INVALID_OPERATION        = 12016;
		public const int ERROR_INTERNET_OPERATION_CANCELLED      = 12017;
		public const int ERROR_INTERNET_INCORRECT_HANDLE_TYPE    = 12018;
		public const int ERROR_INTERNET_INCORRECT_HANDLE_STATE   = 12019;
		public const int ERROR_INTERNET_NOT_PROXY_REQUEST        = 12020;
		public const int ERROR_INTERNET_REGISTRY_VALUE_NOT_FOUND = 12021;
		public const int ERROR_INTERNET_BAD_REGISTRY_PARAMETER   = 12022;
		public const int ERROR_INTERNET_NO_DIRECT_ACCESS         = 12023;
		public const int ERROR_INTERNET_NO_CONTEXT               = 12024;
		public const int ERROR_INTERNET_NO_CALLBACK              = 12025;
		public const int ERROR_INTERNET_REQUEST_PENDING          = 12026;
		public const int ERROR_INTERNET_INCORRECT_FORMAT         = 12027;
		public const int ERROR_INTERNET_ITEM_NOT_FOUND           = 12028;
		public const int ERROR_INTERNET_CANNOT_CONNECT           = 12029;
		public const int ERROR_INTERNET_CONNECTION_ABORTED       = 12030;
		public const int ERROR_INTERNET_CONNECTION_RESET         = 12031;
		public const int ERROR_INTERNET_FORCE_RETRY              = 12032;
		public const int ERROR_INTERNET_INVALID_PROXY_REQUEST    = 12033;
		public const int ERROR_INTERNET_NEED_UI                  = 12034;

		public const int ERROR_INTERNET_HANDLE_EXISTS            = 12036;
		public const int ERROR_INTERNET_SEC_CERT_DATE_INVALID    = 12037;
		public const int ERROR_INTERNET_SEC_CERT_CN_INVALID      = 12038;
		public const int ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR   = 12039;
		public const int ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR   = 12040;
		public const int ERROR_INTERNET_MIXED_SECURITY           = 12041;
		public const int ERROR_INTERNET_CHG_POST_IS_NON_SECURE   = 12042;
		public const int ERROR_INTERNET_POST_IS_NON_SECURE       = 12043;
		public const int ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED  = 12044;
		public const int ERROR_INTERNET_INVALID_CA               = 12045;
		public const int ERROR_INTERNET_CLIENT_AUTH_NOT_SETUP    = 12046;
		public const int ERROR_INTERNET_ASYNC_THREAD_FAILED      = 12047;
		public const int ERROR_INTERNET_REDIRECT_SCHEME_CHANGE   = 12048;
		public const int ERROR_INTERNET_DIALOG_PENDING           = 12049;
		public const int ERROR_INTERNET_RETRY_DIALOG             = 12050;
		public const int ERROR_INTERNET_HTTPS_HTTP_SUBMIT_REDIR  = 12052;
		public const int ERROR_INTERNET_INSERT_CDROM             = 12053;
		public const int ERROR_INTERNET_FORTEZZA_LOGIN_NEEDED    = 12054;
		public const int ERROR_INTERNET_SEC_CERT_ERRORS          = 12055;
		public const int ERROR_INTERNET_SEC_CERT_NO_REV          = 12056;
		public const int ERROR_INTERNET_SEC_CERT_REV_FAILED      = 12057;
		public const int ERROR_FTP_TRANSFER_IN_PROGRESS          = 12110;
		public const int ERROR_FTP_DROPPED                       = 12111;
		public const int ERROR_FTP_NO_PASSIVE_MODE               = 12112;

		public const int ERROR_GOPHER_PROTOCOL_ERROR             = 12130;
		public const int ERROR_GOPHER_NOT_FILE                   = 12131;
		public const int ERROR_GOPHER_DATA_ERROR                 = 12132;
		public const int ERROR_GOPHER_END_OF_DATA                = 12133;
		public const int ERROR_GOPHER_INVALID_LOCATOR            = 12134;
		public const int ERROR_GOPHER_INCORRECT_LOCATOR_TYPE     = 12135;
		public const int ERROR_GOPHER_NOT_GOPHER_PLUS            = 12136;
		public const int ERROR_GOPHER_ATTRIBUTE_NOT_FOUND        = 12137;
		public const int ERROR_GOPHER_UNKNOWN_LOCATOR            = 12138;

		public const int ERROR_HTTP_HEADER_NOT_FOUND             = 12150;
		public const int ERROR_HTTP_DOWNLEVEL_SERVER             = 12151;
		public const int ERROR_HTTP_INVALID_SERVER_RESPONSE      = 12152;
		public const int ERROR_HTTP_INVALID_HEADER               = 12153;
		public const int ERROR_HTTP_INVALID_QUERY_REQUEST        = 12154;
		public const int ERROR_HTTP_HEADER_ALREADY_EXISTS        = 12155;
		public const int ERROR_HTTP_REDIRECT_FAILED              = 12156;
		public const int ERROR_HTTP_NOT_REDIRECTED               = 12160;
		public const int ERROR_HTTP_COOKIE_NEEDS_CONFIRMATION    = 12161;
		public const int ERROR_HTTP_COOKIE_DECLINED              = 12162;
		public const int ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION  = 12168;

		public const int ERROR_INTERNET_SECURITY_CHANNEL_ERROR   = 12157;
		public const int ERROR_INTERNET_UNABLE_TO_CACHE_FILE     = 12158;
		public const int ERROR_INTERNET_TCPIP_NOT_INSTALLED      = 12159;
		public const int ERROR_INTERNET_DISCONNECTED             = 12163;
		public const int ERROR_INTERNET_SERVER_UNREACHABLE       = 12164;
		public const int ERROR_INTERNET_PROXY_SERVER_UNREACHABLE = 12165;

		public const int ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT    = 12166;
		public const int ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT = 12167;
		public const int ERROR_INTERNET_SEC_INVALID_CERT         = 12169;
		public const int ERROR_INTERNET_SEC_CERT_REVOKED         = 12170;

		public const int ERROR_INTERNET_FAILED_DUETOSECURITYCHECK  = 12171;
		public const int ERROR_INTERNET_NOT_INITIALIZED          = 12172;
		public const int ERROR_INTERNET_NEED_MSN_SSPI_PKG          = 12173;
		public const int ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY   = 12174;
		#endregion Constants

//		[STAThread]
//		static void Main(string[] args)
//		{
//			FtpConnection connect = new FtpConnection("test.com", "test", "test");
//			connect.GetFile("test.cs", "C:\\newdave.txt", true, FileAttributes.Normal);
//            connect.Close();
//		}

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern IntPtr InternetOpen(
			[MarshalAs(UnmanagedType.LPTStr)] string agent,
			InternetOpenType accessType,
			[MarshalAs(UnmanagedType.LPTStr)] string lpszProxyName,
			[MarshalAs(UnmanagedType.LPTStr)] string lpszProxyBypass,
			int flags
			);


		[DllImport("WinInet.dll", SetLastError=true)]
		public static extern bool InternetCloseHandle(IntPtr hInternet);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern IntPtr InternetConnect(
											   IntPtr hInternet,
											   [MarshalAs(UnmanagedType.LPTStr)] string serverName,
											   InternetPort serverPort,
											   [MarshalAs(UnmanagedType.LPTStr)] string username,
											   [MarshalAs(UnmanagedType.LPTStr)] string password,
											   InternetService service,
											   int flags,
											   IntPtr context
											   );

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		private static extern bool InternetGetLastResponseInfo(
			out int error,
			[Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer,
			ref int bufferLength
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		private static extern bool FtpGetCurrentDirectory(
			IntPtr hConnect,
			[Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder currentDirectory,
			ref int dwCurrentDirectory
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpSetCurrentDirectory(IntPtr hConnect, string directory);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpCreateDirectory(IntPtr hConnect, string directory);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpRenameFile(IntPtr hConnect, string existing, string newName);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpPutFile(
			IntPtr hConnect,
			string localFile,
			string newRemoteFile,
			FtpTransferType flags,
			IntPtr dwContext
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpGetFile(
			IntPtr hConnect,
			string lpszRemoteFile,
			string lpszNewFile,
			bool fFailIfExists,
			System.IO.FileAttributes flagsAndAttributes,
			FtpTransferType flags,
			IntPtr dwContext
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern IntPtr FtpOpenFile(
			IntPtr hConnect,
			string lpszFileName,
			GenericRights dwAccess,
			FtpTransferType dwFlags,
			IntPtr dwContext
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool InternetReadFile(
			IntPtr hFile,
			[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] byte[] buffer,
			int numberOfBytesToRead,
			out int numberOfBytesRead
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool InternetReadFile(
			IntPtr hFile,
			IntPtr buffer,
			int numberOfBytesToRead,
			out int numberOfBytesRead
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool InternetWriteFile(
			IntPtr hFile,
			[In, MarshalAs(UnmanagedType.LPArray)] byte[] buffer,
			int numberOfBytesToWrite,
			out int numberOfBytesWritten
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool InternetWriteFile(
			IntPtr hFile,
			IntPtr buffer,
			int numberOfBytesToWrite,
			out int numberOfBytesWritten
			);


		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern IntPtr FtpFindFirstFile(
			IntPtr hConnect,
			string lpszSearchFile,
			out Win32FindData lpFindFileData,
			int dwFlags,
			IntPtr dwContext
			);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool InternetFindNextFile(IntPtr hFind, out Win32FindData lpvFindData);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpDeleteFile(IntPtr hConnect, string lpszFileName);

		[DllImport("WinInet.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public static extern bool FtpRemoveDirectory(IntPtr hConnect, string lpszDirectory);

		public static bool FtpGetCurrentDirectory(IntPtr hConnect, out string directory)
		{
			directory = null;
			StringBuilder buffer = new StringBuilder(MAX_PATH);
			int length = buffer.Capacity;
			bool retVal = FtpGetCurrentDirectory(hConnect, buffer, ref length);
			if(retVal)
			{
				directory = buffer.ToString();
			}
			return retVal;
		}

		public static int InternetGetLastResponseInfo(out string message)
		{
			message = ""; //Make the compiler happy
			StringBuilder buffer = new StringBuilder(MAX_PATH);
			int length = buffer.Capacity;
			int errorCode;
			bool retVal = InternetGetLastResponseInfo(out errorCode, buffer, ref length);
			if(!retVal)
			{
				int lastError = Marshal.GetLastWin32Error();
				if(lastError == 122) //ERROR_INSUFFICIENT_BUFFER
				{
					buffer.Capacity = ++length;
					if(InternetGetLastResponseInfo(out errorCode, buffer, ref length))
					{
						message = buffer.ToString();
						return errorCode;
					}
				}

				throw new FtpException(new Win32Exception().Message, lastError);
			}
			else
			{
				message = buffer.ToString();
				return errorCode;
			}

		}

	}
}
